namespace Aphalina.Compression
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Text;

    public class ZipPackage : IDisposable
    {
        private Dictionary<string, FileInfo> files = new Dictionary<string, FileInfo>();
        private bool forRead;
        private Stream stream;
        private object syncObject = new object();

        private ZipPackage(Stream stream, bool forRead)
        {
            this.forRead = forRead;
            this.stream = stream;
            if (forRead)
            {
                this.ReadFiles();
            }
        }

        private void CheckForRead()
        {
            if (!this.forRead)
            {
                throw new InvalidOperationException("OnlyWrite");
            }
        }

        private void CheckForWrite()
        {
            if (this.forRead)
            {
                throw new InvalidOperationException("OnlyRead");
            }
        }

        public void Close()
        {
            ((IDisposable) this).Dispose();
        }

        public static ZipPackage Create(Stream stream)
        {
            return new ZipPackage(stream, false);
        }

        public static ZipPackage Open(Stream stream)
        {
            return new ZipPackage(stream, true);
        }

        public Stream ReadFile(string fileName)
        {
            this.CheckForRead();
            return this.files[fileName].Stream;
        }

        private void ReadFiles()
        {
            BinaryReader reader = new BinaryReader(this.stream);
        Label_000C:
            switch (reader.ReadInt32())
            {
                case 0x4034b50:
                {
                    reader.ReadInt16();
                    reader.ReadInt16();
                    int num2 = reader.ReadInt16();
                    reader.ReadInt16();
                    reader.ReadInt16();
                    uint num3 = reader.ReadUInt32();
                    reader.ReadInt32();
                    int count = reader.ReadInt32();
                    int num5 = reader.ReadInt16();
                    int num6 = reader.ReadInt16();
                    byte[] buffer = new byte[num5];
                    reader.Read(buffer, 0, num5);
                    string key = Encoding.Default.GetString(buffer, 0, buffer.Length);
                    Stream baseStream = reader.BaseStream;
                    baseStream.Position += num6;
                    switch (num2)
                    {
                        case 8:
                        {
                            DecompressStream stream = new DecompressStream(this.stream, false);
                            uint num7 = Crc32.ComputeCrc32(stream);
                            if (num3 != num7)
                            {
                                throw new Exception("InvalidCRC32");
                            }
                            FileInfo info = new FileInfo {
                                Stream = stream,
                                IsCompressed = true
                            };
                            this.files.Add(key, info);
                            break;
                        }
                        case 0:
                        {
                            MemoryStream stream2 = new MemoryStream();
                            byte[] buffer2 = new byte[count];
                            int num8 = reader.BaseStream.Read(buffer2, 0, count);
                            stream2.Write(buffer2, 0, num8);
                            stream2.Position = 0;
                            uint num9 = Crc32.ComputeCrc32(buffer2);
                            if (num3 != num9)
                            {
                                throw new Exception("InvalidCRC32");
                            }
                            FileInfo info2 = new FileInfo {
                                Stream = stream2,
                                IsCompressed = false
                            };
                            this.files.Add(key, info2);
                            break;
                        }
                    }
                    goto Label_000C;
                }
                case 0x8074b50:
                {
                    Stream stream3 = reader.BaseStream;
                    stream3.Position += 12;
                    goto Label_000C;
                }
            }
        }

        void IDisposable.Dispose()
        {
            lock (this.syncObject)
            {
                if (this.stream != null)
                {
                    if (!this.forRead)
                    {
                        this.WriteAllFiles();
                    }
                    this.stream = null;
                }
            }
        }

        private void WriteAllFiles()
        {
            BinaryWriter writer = new BinaryWriter(this.stream);
            BinaryWriter writer2 = new BinaryWriter(new MemoryStream());
            short num = 0;
            foreach (string str in this.files.Keys)
            {
                Stream stream = this.files[str].Stream;
                uint num2 = Crc32.ComputeCrc32(stream);
                int num3 = 0;
                int num4 = (int) stream.Length;
                MemoryStream stream2 = null;
                if (this.files[str].IsCompressed)
                {
                    stream2 = new MemoryStream();
                    CompressStream destination = new CompressStream(stream2, false);
                    stream.Position = 0;
                    stream.CopyTo(destination);
                    destination.Close();
                    if (stream2.Length < stream.Length)
                    {
                        num3 = 8;
                        num4 = (int) stream2.Length;
                    }
                }
                int num5 = (int) this.stream.Position;
                writer.Write(0x4034b50);
                writer.Write((short) 10);
                writer.Write((short) 0);
                writer.Write((short) num3);
                writer.Write((ushort) (DateTime.Now.ToFileTime() >> 0x10));
                writer.Write((ushort) (DateTime.Now.ToFileTime() & 0xffff));
                writer.Write(num2);
                writer.Write(num4);
                writer.Write((int) stream.Length);
                byte[] bytes = Encoding.Default.GetBytes(str);
                writer.Write((short) bytes.Length);
                writer.Write((short) 0);
                writer.Write(bytes);
                if ((stream2 != null) && (stream2.Length < stream.Length))
                {
                    stream2.Position = 0;
                    stream2.CopyTo(this.stream);
                }
                else
                {
                    stream.Position = 0;
                    stream.CopyTo(this.stream);
                }
                writer2.Write(0x2014b50);
                writer2.Write((short) 20);
                writer2.Write((short) 10);
                writer2.Write((short) 0);
                writer2.Write((short) num3);
                writer2.Write((ushort) (DateTime.Now.ToFileTime() >> 0x10));
                writer2.Write((ushort) (DateTime.Now.ToFileTime() & 0xffff));
                writer2.Write(num2);
                writer2.Write(num4);
                writer2.Write((int) stream.Length);
                writer2.Write((short) bytes.Length);
                writer2.Write((short) 0);
                writer2.Write((short) 0);
                writer2.Write((short) 0);
                writer2.Write((short) 0);
                writer2.Write(0x20);
                writer2.Write(num5);
                writer2.Write(bytes);
                num = (short) (num + 1);
            }
            int position = (int) this.stream.Position;
            int length = (short) writer2.BaseStream.Length;
            writer2.Write(0x6054b50);
            writer2.Write((short) 0);
            writer2.Write((short) 0);
            writer2.Write(num);
            writer2.Write(num);
            writer2.Write(length);
            writer2.Write(position);
            writer2.Write((short) 0);
            writer2.BaseStream.Position = 0;
            writer2.BaseStream.CopyTo(this.stream);
        }

        public Stream WriteFile(string fileName, bool compress = true)
        {
            this.CheckForWrite();
            MemoryStream stream = new MemoryStream();
            FileInfo info = new FileInfo {
                Stream = stream,
                IsCompressed = compress
            };
            this.files.Add(fileName, info);
            return stream;
        }

        public string[] FileNames
        {
            get
            {
                return this.files.Keys.ToArray<string>();
            }
        }

        private class FileInfo
        {
            public bool IsCompressed { get; set; }

            public System.IO.Stream Stream { get; set; }
        }
    }
}

